﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;

using BuzzwordBingo.Interface;
using BuzzwordBingo.Interface.API;
using BuzzwordBingo.Entities;

namespace BuzzwordBingo.Game
{
    /// <summary>
    /// Bingo event engine, implements ISetupBingoEvent, IPlayBingoEvent
    /// </summary>
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class Engine : ISetupBingoEvent, IPlayBingoEvent
    {
        #region private members

        private IPersistenceService _persistenceService;
        private IBingoEvent _bingo;
        private IList<IBingoEvent> _bingoList;
        object _listLock = new object();

        #endregion

        #region constructor

        /// <summary>
        /// Initialize with PersistenceService
        /// </summary>
        /// <param name="PersistenceService">Persistence Service implementation</param>
        public Engine(IPersistenceService PersistenceService)
        {
            _persistenceService = PersistenceService;
            CreateNewEvent();
        }

        #endregion

        #region ISetupBingoEvent Members

        [OperationBehavior]
        public void CreateNewEvent()
        {
            _bingo = new BingoEvent();
        }

        [OperationBehavior]
        public void AddParticipant(string Email)
        {
            _bingo.AddParticipant(new Participant(Email));
        }

        [OperationBehavior]
        public void AddDates(DateTime Start, DateTime End)
        {
            _bingo.AddEventDates(Start, End);
        }

        [OperationBehavior]
        public void AddSpeaker(string Speaker)
        {
            _bingo.Speaker = Speaker;
            _bingo.Organiser = Speaker;
        }

        [OperationBehavior]
        public IList<BuzzwordCategoryDto> GetBuzzwordCategoryList()
        {
            return DtoFactory.CreateBuzzwordCategoryListDto(_persistenceService.LoadBuzzwordCategoryList());
        }

        [OperationBehavior]
        public void SelectBuzzwordCategory(int CategoryId)
        {
            _bingo.Category = _persistenceService.LoadBuzzwordCategory(CategoryId);
        }

        [OperationBehavior]
        public void SettleEvent()
        {
            // should check some useful values are actually set
            _persistenceService.SaveEvent(_bingo);
            _bingo = null;
        }

        #endregion

        #region IPlayBingoEvent Members

        [OperationBehavior]
        public BingoEventDto JoinBingoEvent(int EventId)
        {
            IBingoEvent bingo = null;
            if (_bingoList == null)
            {
                lock (_listLock)
                {
                    _bingoList = new List<IBingoEvent>();
                }
            }

            lock (_listLock)
            {
                try
                {
                    bingo = _bingoList.Single(b => b.GetId == EventId);
                }
                catch (System.InvalidOperationException)
                {
                    bingo = _persistenceService.LoadEvent(EventId);
                    _bingoList.Add(bingo);
                }
            }

            if (DateTime.Now < bingo.StartDate || DateTime.Now > bingo.EndDate)
                throw new Exception("Event out of date!");

            return DtoFactory.CreateBingoEventDto(bingo);
        }

        [OperationBehavior]
        public ParticipantDto PlayAs(int EventId, string Email)
        {
            IBingoEvent bingo = null;
            IParticipant theParticipant = null;
            try
            {
                lock (_listLock)
                {
                    bingo = _bingoList.Single(b => b.GetId == EventId);
                }
                theParticipant = bingo.ParticipantList.Single(p => p.Email == Email);
                if(theParticipant.MyBingoCard == null)
                    bingo.CreateBingoCard(theParticipant);
            }
            catch (System.InvalidOperationException opex)
            {
                throw new Exception("Participant not found! " + opex.Message);
            }
            return DtoFactory.CreateParticipantDto(theParticipant);
        }

        [OperationBehavior]
        public void MarkBuzzWord(int EventId, string Email, int Index)
        {
            IBingoEvent bingo = null;
            IParticipant theParticipant = null;
            try
            {
                lock (_listLock)
                {
                    bingo = _bingoList.Single(b => b.GetId == EventId);
                }
                theParticipant = bingo.ParticipantList.Single(p => p.Email == Email);
                bingo.MarkBuzzWord(theParticipant, Index);
            }
            catch (System.InvalidOperationException opex)
            {
                throw new Exception("Participant not found! " + opex.Message);
            }
        }

        [OperationBehavior]
        public ParticipantDto GetWinner(int EventId)
        {
            IBingoEvent bingo = null;
            IParticipant theWinner = null;
            try
            {
                lock (_listLock)
                {
                    bingo = _bingoList.Single(b => b.GetId == EventId);
                }
                theWinner = bingo.GetWinner();
            }
            catch (System.InvalidOperationException opex)
            {
                throw new Exception("Winner not found! " + opex.Message);
            }
            return DtoFactory.CreateParticipantDto(theWinner);
        }

        #endregion
    }
}
